/**
 * Collaborative Story Development Web Application (CSDApp) 
 * Copyright Anton Strack 2014
 *
 * This file is part of Collaborative Story Development Application (CSDApp).
 *
 * CSDApp is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * CSDApp is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * CSDApp. If not, see <http://www.gnu.org/licenses/>.
 *
 */
package csdwa;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This class is a special type of dao that primarily maps two tables together
 * that share a many to many relationship. This class uses the dao's of the
 * respective tables to select the values from those tables and it also contains
 * it's own dao functions for the mapping table itself. However, this mapper is
 * intended to be used as a one to many mapping and thus, only one of the
 * related daos can be used at a time.
 *
 * @author Anton Strack
 */
public class NotificationRecipientMapperDao extends DaoSupport {

    /**
     * A list of the beans that make up the mapping between the two other
     * tables.
     */
    private List<NotificationRecipientMap> beans;
    /**
     * The notification template objects that map to the a user object.
     */
    private CsdwaDao<NotificationTemplate> ntdao;
    /**
     * The user object that maps to a template object
     */
    private CsdwaDao<User> udao;

    /**
     * Construct the RoleDao
     *
     * @param conn the pre-configured connection to the database.
     */
    public NotificationRecipientMapperDao(Connection conn) {
        super(conn);
        this.beans = new ArrayList<NotificationRecipientMap>();
        this.ntdao = new NotificationTemplateDao(conn);
        this.udao = new UserDao(conn);
        this.restoreQueryMarkers();
    }

    /**
     * Select all the beans from the bean table
     *
     * @return the row count indicating if 0 or more beans were retrieved from
     * the database.
     */
    public int selectBeans() {
        int result = 0;
        this.usingQueryMarkers = this.getCoreSelectUsingQueryMarkers();
        this.setMarker(QueryMarker.CORE_CONDITIONS, "1=1");
        try {
            ps = getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, SELECT_QUERY));
            result = executeSelectQuery(ps);
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Select a single bean by it's id.
     *
     * @param id the id number of the bean to select.
     * @return 1 or 0, indicating a bean was retrieved or not.
     */
    public int selectBeanById(int ntId, int uId) {
        int result = 0;
        this.usingQueryMarkers = this.getCoreSelectUsingQueryMarkers();
        this.setMarker(QueryMarker.CORE_CONDITIONS, "NotificationTypeID = ? AND UserID = ?");
        try {
            ps = getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, SELECT_QUERY));
            this.ps.setInt(1, ntId);
            this.ps.setInt(2, uId);
            result = executeSelectQuery(ps);
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Select a single bean by it's id.
     *
     * @param id the id number of the bean to select.
     * @return 1 or 0, indicating a bean was retrieved or not.
     */
    public int selectBeansByUserId(int uId) {
        int result = 0;
        this.usingQueryMarkers = this.getCoreSelectUsingQueryMarkers();
        this.setMarker(QueryMarker.CORE_CONDITIONS, "UserID = ?");
        try {
            ps = getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, SELECT_QUERY));
            this.ps.setInt(1, uId);
            result = executeSelectQuery(ps);
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Select a single bean by it's id.
     *
     * @param id the id number of the bean to select.
     * @return 1 or 0, indicating a bean was retrieved or not.
     */
    public int selectBeansByNotifcationTemplateId(int ntId) {
        int result = 0;
        this.usingQueryMarkers = this.getCoreSelectUsingQueryMarkers();
        this.setMarker(QueryMarker.CORE_CONDITIONS, "NotificationTypeID = ?");
        try {
            ps = getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, SELECT_QUERY));
            this.ps.setInt(1, ntId);
            result = executeSelectQuery(ps);
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Save a bean to the database
     *
     * @param bean the bean to insert. Since this is a mapping table, both
     * foreign keys are required to be set in the bean.
     * @return 1 if success or 0 on failure.
     */
    public int insertBean(NotificationRecipientMap bean) {
        int result = 0;
        ResultSet rs;
        this.usingQueryMarkers = new ArrayList<QueryMarker>();
        this.usingQueryMarkers.add(QueryMarker.CORE_COLUMNS);
        this.usingQueryMarkers.add(QueryMarker.CORE_TABLE);
        this.usingQueryMarkers.add(QueryMarker.INSERT_VALUES);
        /*
         * HSQLDB cannot properly handle insert statements that have table identifiesr in the form of table.columnName so we need to remove the table name for inserts.
         */
        this.queryMarkers.put(QueryMarker.CORE_COLUMNS, this.prepareInsertColumns(this.getMarker(QueryMarker.CORE_COLUMNS)));
        try {
            this.ps = this.getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, INSERT_QUERY));
            this.ps.setInt(1, bean.getNotificationTypeId());
            this.ps.setInt(2, bean.getUserId());
            result = this.ps.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Update a bean in the database
     *
     * @param bean the bean to update.
     * @return 1 or 0 indicating if the bean was updated or not.
     */
    public int updateBean(NotificationRecipientMap bean) {
        throw new UnsupportedOperationException("NotificationRecipient table cannot be updated.");
    }

    /**
     * Deletes the bean from the database
     *
     * @param bean the bean to delete
     * @return 1 or 0 indicating if the bean was deleted or not.
     */
    public int deleteBean(NotificationRecipientMap bean) {
        int result = 0;
        this.usingQueryMarkers = new ArrayList<QueryMarker>();
        this.usingQueryMarkers.add(QueryMarker.CORE_CONDITIONS);
        this.usingQueryMarkers.add(QueryMarker.CORE_TABLE);
        this.setMarker(QueryMarker.CORE_CONDITIONS, "NotificationTypeID = ? AND UserID = ?");
        try {
            ps = getConn().prepareStatement(this.buildQueryString(usingQueryMarkers, DELETE_QUERY));
            this.ps.setInt(1, bean.getNotificationTypeId());
            this.ps.setInt(2, bean.getUserId());
            result = this.ps.executeUpdate();
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * Get a list of beans
     *
     * @return a list of bean objects
     */
    public List<NotificationRecipientMap> getList() {
        return new ArrayList<NotificationRecipientMap>(this.beans);
    }

    /**
     * Add a bean in the dao's bean list.
     *
     * @param bean the bean to add or update
     */
    public void addBeanToDao(NotificationRecipientMap bean) {
        this.beans.add(bean);
    }

    /**
     * Reset the query markers to their default values for the dao.
     */
    @Override
    public void restoreQueryMarkers() {
        this.setMarker(QueryMarker.CORE_TABLE, "notificationrecipient");
        this.setMarker(QueryMarker.CORE_COLUMNS, "NotificationTypeID, UserID");
        this.setMarker(QueryMarker.CORE_CONDITIONS, "NotificationTypeID = ? AND UserID = ?");
        this.setMarker(QueryMarker.ORDER_BY_COLUMN, "UserID");
        this.setMarker(QueryMarker.ORDER_BY_MODE, "ASC");
        this.setMarker(QueryMarker.LIMIT, "");
        this.setMarker(QueryMarker.INSERT_VALUES, "?, ?");
        this.setMarker(QueryMarker.UPDATE_COLUMNS_VALUES, "");
    }

    /**
     * Execute the select query and place the results in beans list.
     *
     * @param ps the fully prepared PreparedStatement
     * @return the row count from the select statement
     */
    @Override
    protected int executeSelectQuery(PreparedStatement ps) {
        int result = 0;
        ResultSet rs;
        try {
            rs = ps.executeQuery();
            while (rs.next()) {
                NotificationRecipientMap bean = new NotificationRecipientMap();
                bean.setNotificationTypeId(rs.getInt("NotificationTypeID"));
                bean.setUserId(rs.getInt("UserID"));
                this.beans.add(bean);
                result++;
            }
        } catch (SQLException ex) {
            Logger.getLogger(NotificationRecipientMapperDao.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }

    /**
     * The notification template objects that map to the a user object.
     *
     * @return the ntdao
     */
    public CsdwaDao<NotificationTemplate> getNotificationTemplateDao() {
        return ntdao;
    }

    /**
     * The notification template objects that map to the a user object.
     *
     * @param ntdao the ntdao to set
     */
    public void setNotificationTemplateDao(CsdwaDao<NotificationTemplate> ntdao) {
        this.ntdao = ntdao;
    }

    /**
     * The user object that maps to a template object
     *
     * @return the udao
     */
    public CsdwaDao<User> getUserDao() {
        return udao;
    }

    /**
     * The user object that maps to a template object
     *
     * @param udao the udao to set
     */
    public void setUserDao(CsdwaDao<User> udao) {
        this.udao = udao;
    }

    /* mapping related methods */
    public int selectUsersByNotificationTemplateId(int ntId, boolean useRelatedMarkers) {
        int result = 0;
        // col, table, condition
        this.udao.setUseRelatedMarkers(useRelatedMarkers);
        this.udao.addDerivedValue("UserID", "notificationrecipient", "useraccount.ID = notificationrecipient.UserID AND NotificationTypeID =" + ntId);
        result = this.udao.selectBeans();
        return result;
    }

    public int selectNotificationTemplatesByUserId(int uId, boolean useRelatedMarkers) {
        int result = 0;
        // col, table, condition
        this.ntdao.setUseRelatedMarkers(useRelatedMarkers);
        this.ntdao.addDerivedValue("NotificationTypeID", "notificationrecipient", "notificationtype.ID = notificationrecipient.NotificationTypeID AND UserID = " + uId);
        result = this.ntdao.selectBeans();
        return result;
    }
}
